package com.labbit.platform.commons.utils;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Description
 * @Date 2019-07-28 20:02
 * @Author CHEN YU
 **/
@Slf4j
public class Profiler {

    private static volatile boolean debug = false;
    /**
     * 显示日志格式
     */
    private final static String LOG_TEMPLATE = "[messag=%s][startTime=%s][endTime=%s][durationTime=%sms][processors=%s][memUse=%s]";

    /**
     * 简单日志格式
     */
    private final static String SIMPLE_LOG_TEMPLATE = "[durationTime=%sms][message=%s]";

    /**
     * 日期格式
     */
    private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /**
     * profile日志,建议运行中别做修改,否则有些配置会导致残留线程
     */
    private static ThreadLocal<ProfileConfig> configHolder = new ThreadLocal<ProfileConfig>() {
        @Override
        protected ProfileConfig initialValue() {
            return new ProfileConfig(true, false, 0);
        }
    };

    /**
     * 开始monitor的时间
     */
    private static ThreadLocal<Stack<MonitorResource>> resStackHolder = new ThreadLocal<Stack<MonitorResource>>() {
        @Override
        protected Stack<MonitorResource> initialValue() {
            return new Stack<MonitorResource>();
        }
    };

    /**
     * 监控线程
     */
    private static ThreadLocal<MonitorThread> monitorThreadHolder = new ThreadLocal<MonitorThread>();

    /**
     * 开始monitor
     */
    public static void enter(Object msgObj) {

        resStackHolder.get().push(new MonitorResource(msgObj, System.currentTimeMillis()));
        ProfileConfig config = configHolder.get();
        // 开启监控线程
        if (config.isUseMonitorThread()) {
            if (monitorThreadHolder.get() != null) {
                killThread();
            }
            MonitorThread monitorThread = new MonitorThread(getCurrentMonitorRes(), config);
            monitorThreadHolder.set(monitorThread);
            monitorThread.start();
        }
    }

    /**
     * 结束monitor
     */
    public static MonitorResource release() {
        try {
            Stack<MonitorResource> monitorResStack = resStackHolder.get();
            MonitorResource monitorResource = getCurrentMonitorRes();
            monitorResource.setEndTime(System.currentTimeMillis());
            ProfileConfig config = configHolder.get();
            // 监控线程关闭
            if (config.isUseMonitorThread()) {
                killThread();
            }
            return monitorResStack.pop();
        } catch (Throwable e) {
            return new MonitorResource(e.getMessage(), 0);
        }
    }

    /**
     * 使用新的messageObj替换原来的
     */
    public static MonitorResource release(Object messageObj) {
        MonitorResource monitorResource = release();
        monitorResource.setMessageObj(messageObj);
        return monitorResource;
    }

    /**
     * 结束monitor并且打印日志
     */
    public static MonitorResource releaseAndLog(Object messageObj) {
        MonitorResource resource = release(messageObj);
        if (log.isDebugEnabled()) {
            log.info(resource.toString());
        }
        return resource;
    }

    /**
     * 结束monitor并且打印日志
     */
    public static MonitorResource releaseAndLog() {
        MonitorResource resource = release();
        if (log.isDebugEnabled()) {
            log.info(resource.toString());
        }
        return resource;
    }

    /**
     * 设置profile配置
     *
     * @param config
     */
    public static void setProfileConfig(ProfileConfig config) {
        configHolder.set(config);
    }

    /**
     * 移除监控线程
     */
    private static void killThread() {

        MonitorThread futureTask = monitorThreadHolder.get();
        monitorThreadHolder.remove();
        futureTask.interrupt();
    }

    /**
     * 获取当前的monitorRes
     */
    public static MonitorResource getCurrentMonitorRes() {
        try {
            Stack<MonitorResource> resStack = resStackHolder.get();
            return resStack.get(resStack.size() - 1);
        } catch (Exception e) {
            return new MonitorResource(e.getMessage(), 0);
        }
    }

    /**
     * 资源使用情况，比如cpu最大使用量等。
     *
     * @author Xiang QuanChao
     * @date 18/1/28 上午1:13
     */
    public static class MonitorResource {

        /**
         * 当前资源的标志
         */
        private Object messageObj = null;

        /**
         * 开始时间
         */
        private long startTime = 0;

        /**
         * 结束时间
         */
        private long endTime = 0;

        /**
         * 执行次数
         */
        private int processorNums = 0;

        /**
         * 内存使用情况
         */
        private List<Long> memUse = Lists.newArrayList();

        /**
         * 消息分隔符
         */
        private static String MSG_SEPARATOR = ",";


        public MonitorResource(Object messageObj, long startTime) {
            super();
            this.messageObj = messageObj;
            this.startTime = startTime;
        }

        public void setMessageObj(Object messageObj) {
            this.messageObj = messageObj;
        }

        public String getMemUse() {
            if (CollectionUtils.isEmpty(memUse)) {
                return StringUtils.EMPTY;
            }
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < memUse.size(); i++) {
                stringBuilder.append(FileSizeHelper.getHumanReadableFileSize(memUse.get(i), FileSizeHelper.ONE_KB, FileSizeHelper.KB));
                if (i != memUse.size() - 1) {
                    stringBuilder.append(MSG_SEPARATOR);
                }
            }
            return stringBuilder.toString();
        }

        /**
         * 获取整个profile堆栈
         */
        public Stack<MonitorResource> getMonitorResStack() {
            return resStackHolder.get();
        }

        @Override
        public String toString() {
            return configHolder.get().isUseSimpleLogTemplate()
                    ? (String.format(SIMPLE_LOG_TEMPLATE, getDurTime(), messageObj))
                    : (String.format(LOG_TEMPLATE, messageObj, DATE_FORMAT.format(new Date(startTime)),
                    DATE_FORMAT.format(new Date(endTime)), getDurTime(), processorNums,
                    getMemUse()));
        }

        /**
         * 获取运行时间
         */
        public long getDurTime() {
            return endTime - startTime;
        }

        public void putMemUse(long l) {
            memUse.add(l);
        }

        public void setEndTime(long endTime) {
            this.endTime = endTime;
        }

        public Object getMessageObj() {
            return messageObj;
        }

        public void setProcessorNums(int processorNums) {
            this.processorNums = processorNums;
        }
    }

    /**
     * Profile 配置类
     */
    private static class ProfileConfig {

        /**
         * 使用模板日志log模板
         */
        private boolean useSimpleLogTemplate = false;

        /**
         * 使用后台线程
         */
        private boolean useMonitorThread = false;

        /**
         *
         */
        private int monitorCollectDurTime = 500;

        public ProfileConfig(boolean useSimpleLogTemplate, boolean useMonitorThread,
                             int monitorCollectDurTime) {
            super();
            this.useSimpleLogTemplate = useSimpleLogTemplate;
            this.useMonitorThread = useMonitorThread;
            this.monitorCollectDurTime = monitorCollectDurTime;
        }

        public boolean isUseSimpleLogTemplate() {
            return useSimpleLogTemplate;
        }

        public void setUseSimpleLogTemplate(boolean useSimpleLogTemplate) {
            this.useSimpleLogTemplate = useSimpleLogTemplate;
        }

        public boolean isUseMonitorThread() {
            return useMonitorThread;
        }

        public void setUseMonitorThread(boolean useMonitorThread) {
            this.useMonitorThread = useMonitorThread;
        }

        public int getMonitorCollectDurTime() {
            return monitorCollectDurTime;
        }

        public void setMonitorCollectDurTime(int monitorCollectDurTime) {
            this.monitorCollectDurTime = monitorCollectDurTime;
        }
    }

    /**
     * 监控线程
     */
    private static class MonitorThread extends Thread {

        /**
         * 线程个数
         */
        private static final AtomicLong threadCount = new AtomicLong();

        /**
         * 资源使用情况
         */
        private MonitorResource monitorResource;

        /**
         * 配置情况
         */
        private final ProfileConfig config;

        public MonitorThread(MonitorResource resource, ProfileConfig config) {
            monitorResource = resource;
            setName("monitor-thread-" + threadCount.getAndIncrement());
            setDaemon(true);
            this.config = config;
        }

        /**
         * @see Thread#run()
         */
        @Override
        public void run() {
            monitorResource.setProcessorNums(Runtime.getRuntime().availableProcessors());
            while (true) {
                monitorResource.putMemUse(
                        Runtime.getRuntime().maxMemory() - Runtime.getRuntime().freeMemory());
                try {
                    Thread.sleep(config.getMonitorCollectDurTime());
                } catch (InterruptedException e) {
                    return;
                }
            }
        }
    }
}
